IMPLEMENTATION MODULE Tad_Lista;

FROM Storage IMPORT ALLOCATE, DEALLOCATE;
FROM SYSTEM  IMPORT TSIZE;
FROM StrIO    IMPORT WriteString, WriteLn; 
FROM NumberIO IMPORT WriteInt; 

TYPE 
    Lista = POINTER TO Lista_Cabeza;

    Lista_Cabeza = RECORD
                      tamanio : CARDINAL;
                      primero : POINTER TO Caja;
                      ultimo  : POINTER TO Caja;
                      imprimir: imprimirElemen;
                   END;

    Caja = RECORD
              ant: POINTER TO Caja;
              elem : ADDRESS;               
              sig: POINTER TO Caja;
           END;

					

(*
  Procedimeinto: Lista_Crear
  Descripcion: Crea una lista vacia
  Parametros: la funcion de impresion de los elementos que 
              contendra la lista
  Retorno:  la lista creada
  Pre condiciones: true
  Post condiciones: retorna una lista creada
*)
PROCEDURE Crear_Lista(funcion: imprimirElemen) : Lista;
VAR l : Lista;
BEGIN
   NEW(l);

   (* Verificando que se haya obtenido la memoria *)
   IF (l = NIL) THEN
      RETURN NIL;
   END;

	   l^.tamanio := 0;
	   l^.primero := NIL;
	   l^.ultimo  := NIL;
	   l^.imprimir := funcion;

   RETURN l; 
END Crear_Lista;    

PROCEDURE Lista_Agregar(VAR l: Lista; elemento : ADDRESS) : BOOLEAN;
VAR r : BOOLEAN;
    c : POINTER TO Caja;
BEGIN

	IF (l = NIL) THEN
		RETURN FALSE;
	END;

	NEW(c);
	IF (c=NIL) THEN
		RETURN FALSE;
	END;

	c^.ant := NIL;
	c^.elem := elemento;
	c^.sig := NIL;

   IF (l^.tamanio = 0) THEN (* si la lista esta vacia *)
      l^.primero := c;
      l^.ultimo  := c;
      c^.ant := NIL;
      c^.sig := NIL;
   ELSE  (* La lista tiene al menos un elemento, el nuevo 
            elemento va al final*)
      l^.ultimo^.sig := c;
      c^.ant := l^.ultimo;
      c^.sig := NIL;
      l^.ultimo  := c;
   END;

   l^.tamanio := l^.tamanio + 1;

   RETURN TRUE;
END Lista_Agregar;   

PROCEDURE Lista_Obtener(l: Lista; pos : CARDINAL) : ADDRESS ;
VAR c: POINTER TO Caja; 
    e : ADDRESS;
    i : CARDINAL;
BEGIN

	IF (l = NIL) OR (pos >= l^.tamanio) OR (pos < 0)  THEN
		RETURN NIL;
	END;
	c := l^.primero;
   
	i := 0;
	WHILE (i < pos) DO
		i := i + 1;
		c := c^.sig;	
	END;
    RETURN c^.elem;
END Lista_Obtener;



PROCEDURE Lista_Eliminar(l: Lista; pos : CARDINAL) : BOOLEAN;
VAR c: POINTER TO Caja;
VAR e : ADDRESS; 
BEGIN
   IF (l = NIL) OR (pos > l^.tamanio) OR (pos < 0) THEN
      RETURN FALSE;
   END;

   c := l^.primero;

   (* eliminando el primer elemento*)
   IF (pos = 0) THEN
      e := l^.primero^.elem;
      l^.primero := l^.primero^.sig;
      l^.ultimo^.sig := l^.primero;
      l^.primero^.ant := l^.ultimo;
      l^.tamanio := l^.tamanio -1;
      DISPOSE(c);
      RETURN TRUE;   



    ELSE
        
      (*  Hay que pararse en el elemento a borrar *)
      WHILE ((c <> NIL) AND (pos > 0)) DO
          c := c^.sig;
          pos := pos - 1;
      END;

      e := c^.elem;
      c^.ant^.sig := c^.sig;
      c^.sig^.ant := c^.ant;
      l^.tamanio := l^.tamanio -1;

      (* caso en que el elemento a eliminar sea el ultimo*)
      IF (c = l^.ultimo) THEN
        l^.ultimo := c^.ant;
      END;

      DISPOSE(c);
      RETURN TRUE;
       
    END;

END Lista_Eliminar;


PROCEDURE Lista_Tamanio(l: Lista)  : CARDINAL;
BEGIN
   RETURN l^.tamanio;
END Lista_Tamanio;

PROCEDURE Lista_Destruir(l: Lista);
VAR c,ca : POINTER TO Caja;
VAR i: CARDINAL;
BEGIN
   c := l^.primero;
   i := 0;
   WHILE (i < l^.tamanio) DO
      (*  No Debemos hace destruir del elemento,
          en este caso eso es labor del Main *)
       ca := c;
       c := c^.sig;
       i := i + 1;
       
       DISPOSE(ca);
   END;
   DISPOSE(l);
END Lista_Destruir;


PROCEDURE Lista_Imprimir(l: Lista);
VAR c : POINTER TO Caja;
VAR pos : CARDINAL; 

BEGIN
	IF (l = NIL) THEN
		RETURN;
	END;
	c := l^.primero;

	WriteString("[");
	WriteInt(l^.tamanio, 3);
	WriteString("] = <" );
   
	pos := 0;
	WHILE (pos < l^.tamanio) DO
                WriteLn();
		l^.imprimir(c^.elem);
		c := c^.sig;
		pos := pos + 1;
	END;
	WriteLn();
	WriteString(">");
	WriteLn();
END Lista_Imprimir;

END Tad_Lista.
